/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package elderberryni2.panels;

import elderberryni2.Main;
import elderberryni2.data.TelemetryDataSet;
import elderberryni2.data.container.DataContainer;
import elderberryni2.data.container.LocationContainer;
import elderberryni2.exceptions.NoSuchTimeableArrayListException;
import elderberryni2.interfaces.IChartSavable;
import elderberryni2.interfaces.IDataCollecting;
import elderberryni2.utils.Utils;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JSlider;

/**
 *
 * @author warringer
 */
public class GroundPathPanel extends javax.swing.JPanel implements Runnable, 
        IDataCollecting, IChartSavable {

    private enum PlanetaryBody { Mun, Kerbin, Minmus }
    
    /**
     * Creates new form GroundPathPanel
     */
    public GroundPathPanel(Main parent) {
        this.parent = parent;
        initComponents();
        initDisplay();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        groundPathLabel = new javax.swing.JLabel();
        jLabel1 = new javax.swing.JLabel();
        displayTimeLabel = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        displayTimeSlider = new javax.swing.JSlider();
        timeDisplayLabel = new javax.swing.JLabel();
        saveImageButton = new javax.swing.JButton();
        saveAllButton = new javax.swing.JButton();

        setPreferredSize(new java.awt.Dimension(765, 520));

        groundPathLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/elderberryni2/ressources/Kerbin.png"))); // NOI18N

        jLabel1.setText("Displayed Time");

        displayTimeLabel.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);

        org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, displayTimeSlider, org.jdesktop.beansbinding.ELProperty.create("${value}"), displayTimeLabel, org.jdesktop.beansbinding.BeanProperty.create("text"));
        bindingGroup.addBinding(binding);

        jLabel3.setText("s");

        displayTimeSlider.setMajorTickSpacing(1000);
        displayTimeSlider.setMaximum(72000);
        displayTimeSlider.setMinimum(3600);
        displayTimeSlider.setMinorTickSpacing(100);
        displayTimeSlider.setPaintTicks(true);
        displayTimeSlider.setSnapToTicks(true);
        displayTimeSlider.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                displayTimeSliderStateChanged(evt);
            }
        });

        timeDisplayLabel.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
        timeDisplayLabel.setText("0d:01h:00m:00s");

        saveImageButton.setText("Save Image");
        saveImageButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveImageButtonActionPerformed(evt);
            }
        });

        saveAllButton.setText("Save All");
        saveAllButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveAllButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(groundPathLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 741, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel1)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                .addComponent(displayTimeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel3)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                .addComponent(timeDisplayLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 123, javax.swing.GroupLayout.PREFERRED_SIZE))
                            .addComponent(displayTimeSlider, javax.swing.GroupLayout.PREFERRED_SIZE, 298, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(saveImageButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(saveAllButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(groundPathLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(displayTimeLabel)
                    .addComponent(jLabel3)
                    .addComponent(timeDisplayLabel)
                    .addComponent(saveImageButton))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(displayTimeSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(saveAllButton))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        bindingGroup.bind();
    }// </editor-fold>//GEN-END:initComponents

    private void displayTimeSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_displayTimeSliderStateChanged
        JSlider source = (JSlider) evt.getSource();
        if (!source.getValueIsAdjusting()) {
            int size = (int) source.getValue();
            timeDisplayLabel.setText(Utils.formatTime(size));
            maxTimeDifference = new Float(size);
            locationList.setMaxTimeDifference(maxTimeDifference);
        }
    }//GEN-LAST:event_displayTimeSliderStateChanged

    private void saveImageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveImageButtonActionPerformed
        saveChart();
    }//GEN-LAST:event_saveImageButtonActionPerformed

    private void saveAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAllButtonActionPerformed
        parent.saveAllCharts();
    }//GEN-LAST:event_saveAllButtonActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel displayTimeLabel;
    private javax.swing.JSlider displayTimeSlider;
    private javax.swing.JLabel groundPathLabel;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JButton saveAllButton;
    private javax.swing.JButton saveImageButton;
    private javax.swing.JLabel timeDisplayLabel;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration//GEN-END:variables
    
    private Main parent = null;

    private TelemetryDataSet<LocationContainer, Float> locationList = null;
    
    private ArrayList<Point2D.Float> pointList = null;
    
    private BufferedImage kerbin, minmus, mun;
    
    private String saveFolder;
    
    private Float maxTimeDifference = 3600f;
    
    private PlanetaryBody body = PlanetaryBody.Kerbin;
    
    private void initDisplay() {
        if (locationList == null) {
            locationList = new TelemetryDataSet<LocationContainer, Float>();
        }
        try {
            kerbin = ImageIO.read(getClass().getResource("/elderberryni2/ressources/Kerbin.png"));
            minmus = ImageIO.read(getClass().getResource("/elderberryni2/ressources/Minmus.png"));
            mun = ImageIO.read(getClass().getResource("/elderberryni2/ressources/Mun.png"));
        } catch (IOException ex) {
            Logger.getLogger(GroundPathPanel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void run() {
        while (true) {
            drawImage(makeMap());
        }
    }

    @Override
    public synchronized void newDataPoint(DataContainer dataPoint) {
        PlanetaryBody newBody = PlanetaryBody.Kerbin;
        if ("Mun".equals(dataPoint.getBody())) {
            newBody = PlanetaryBody.Mun;
        }
        if ("Minmus".equals(dataPoint.getBody())) {
            newBody = PlanetaryBody.Minmus;
        }
        if (newBody != body) {
            locationList.clear();
            body = newBody;
        }
        locationList.add(dataPoint.getUuid(), dataPoint.getLocation());
        locationList.setMaxTimeDifference(maxTimeDifference);
        locationList.setMinTimeDifference(10f);
    }

    private synchronized void createPointsList(String uuid) throws NoSuchTimeableArrayListException {
        int altLongitude = 0;
        for (LocationContainer location : locationList.getDataList(uuid)) {
            if (pointList == null) {
                pointList = new ArrayList<Point2D.Float>();
            }
            int latitude = new Double(location.getLatitude()).intValue();
            int longitude = new Double(location.getLongitude()).intValue() + 3780;
            longitude = longitude % 360 - 180;
            float pos_x = 320 + (longitude * 640 / 360);
            float pos_xAlt = 320 + (longitude * 640 / 360);
            float pos_y = 240 + (-1 * latitude * 480 / 180);
            if (pos_y > 480) {
                pos_y = 480;
            } else if (pos_y < 0) {
                pos_y = 0;
            }
            if ((altLongitude > 0 && longitude < 0) || (altLongitude < 0 && longitude > 0)) {
                Point2D.Float point = new Point2D.Float(640, pos_y);
                pointList.add(point);
                point = new Point2D.Float(0, pos_y);
                pointList.add(point);
            } else {
                if (pos_x > 640) {
                    pos_x = 640;
                } else if (pos_x < 0) {
                    pos_x = 0;
                }
                Point2D.Float point = new Point2D.Float(pos_x, pos_y); 
                pointList.add(point);
            }
            altLongitude = longitude;
        }
    }

    static BufferedImage deepCopy(BufferedImage bi) {
        ColorModel cm = bi.getColorModel();
        boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
        WritableRaster raster = bi.copyData(null);
        return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
    }

    private Graphics2D drawPath(Graphics2D g2d) {
        
        g2d.setColor(Color.red);
        Float alt_x = null, alt_y = null;
        for (Point2D.Float point : pointList) {
            if (alt_x == null && alt_y == null) {
                alt_x = point.x;
                alt_y = point.y;
            }
            if (alt_x != 640) {
                g2d.draw(new Line2D.Float(alt_x, alt_y, point.x, point.y));
            }
            alt_x = point.x;
            alt_y = point.y;
        }
        pointList = null;
        return g2d;
    }

    private void drawImage(BufferedImage map) {
        groundPathLabel.setIcon(new ImageIcon(map));
    }

    @Override
    public void saveChart() {
        Long epoch = new Long(System.currentTimeMillis()/1000);
        File file = new File(this.saveFolder + "GroundPath_" + epoch.toString() + ".png");
        try {
            ImageIO.write(makeMap(), "png", file);
        } catch (IOException ex) {
            Logger.getLogger(GroundPathPanel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void setSaveFolder(String saveFolder) {
        this.saveFolder = saveFolder;
    }
    
    public void clear() {
        locationList.clear();
    }
    
    private BufferedImage getBodyImage() {
        switch (body) {
        case Minmus:
            return deepCopy(minmus);
        case Mun:
            return deepCopy(mun);
        case Kerbin:
        default:
            return deepCopy(kerbin);
        }
    }
    
    private BufferedImage makeMap() {
        Set<String> parts = locationList.getParts();
        BufferedImage map = getBodyImage();
        Graphics2D g2d = map.createGraphics();
        for (String part : parts) {
            try {
                createPointsList(part);
            } catch (NoSuchTimeableArrayListException ex) {
                Logger.getLogger(GroundPathPanel.class.getName()).log(Level.SEVERE, null, ex);
            }
            g2d = drawPath(g2d);
        }
        g2d.dispose();
        return map;
    }
}
